/**
 * 
 */
package grid;

import java.util.*;
import java.io.*;
import org.jgap.Genotype;

/**
 * @author Darko
 * 
 */
public class JobGraphMigrators {

    private int islands;
    private int epochs;
    private int seasons;
    private int migrationWidth;
    private List<Genotype> populations;
    private String type = "\"DAG\"";
    private String vo = "seegrid";
    private String outputDir = "";
    private String requirements;
    private String breederExec;
    private String migratorExec;

    public JobGraphMigrators() {
    }

    public JobGraphMigrators(int islands, int epochs, int seasons, int migrationWidth) {
        this.islands = islands;
        this.epochs = epochs;
        this.seasons = seasons;
        this.migrationWidth = migrationWidth;
    }

    public String getVO() {
        return "\"" + this.vo + "\"";
    }

    public void setVO(String vo) {
        this.vo = vo;
    }

    public String getOutputDir() {
        return outputDir + File.separator;
    }

    public void setOutputDir(String outputDir) {
        this.outputDir = outputDir;
    }

    public String getRequirements() {
        return requirements;
    }

    public void addRequirement(String req) {
        requirements = requirements == null ? req : requirements + "||" + req;
    }
    private String version;

    public String getVersion() {
        return this.version;
    }

    public void setVersion(String version) {
        this.version = version;
    // setRequirements();
    }
    private String rank = "-other.GlueCEStateEstimatedResponseTime";

    public String getRank() {
        return this.rank;
    }

    public void setRank(String rank) {
        this.rank = rank;
    }
    private int defaultNodeRetryCount = 0;
    private int defaultNodeShallowRetryCount = 0;

    public int getDefaultNodeRetryCount() {
        return this.defaultNodeRetryCount;
    }

    public void setDefaultNodeRetryCount(int dnrc) {
        if (dnrc < 0) {
            throw new IllegalArgumentException("retry count can not be less than zero");
        }
        this.defaultNodeRetryCount = dnrc;
    }

    public int getDefaultNodeShallowRetryCount() {
        return this.defaultNodeShallowRetryCount;
    }

    public void setDefaultNodeShallowRetryCount(int dnsrc) {
        if (dnsrc < 0) {
            throw new IllegalArgumentException("retry count can not be less than zero");
        }
        this.defaultNodeShallowRetryCount = dnsrc;
    }
    private String executable;

    public String getExecutable() {
        return "\"" + this.executable + "\"";
    }

    public void setExecutable(String executable) {
        this.executable = executable;
    }

    public String getBreederExec() {
        return breederExec;
    }

    public void setBreederExec(String breederExec) {
        this.breederExec = breederExec;
    }

    public String getMigratorExec() {
        return migratorExec;
    }

    public void setMigratorExec(String migratorExec) {
        this.migratorExec = migratorExec;
    }
    private String inputSandbox;

    public String getInputSandbox() {
        String isb = "";//{\"" + executable + "\"";
        if (inputSandbox != null) {
            isb += ", " + inputSandbox;
        }
        isb += "}";
        return "{" + isb.substring(2);
    }

    public void addInput(String inputFile) {
        if (inputSandbox != null) {
            inputSandbox += ", " + "\"" + inputFile + "\"";
        } else {
            inputSandbox = "\"" + inputFile + "\"";
        }
    }

    public int getEpochs() {
        return this.epochs;
    }

    public void setEpochs(int epochs) {
        this.epochs = epochs;
    }

    public int getIslands() {
        return this.islands;
    }

    public void setIslands(int islands) {
        this.islands = islands;
    }

    public int getMigrationWidth() {
        return this.migrationWidth;
    }

    public void setMigrationWidth(int migrationWidth) {
        this.migrationWidth = migrationWidth;
    }

    public int getSeasons() {
        return this.seasons;
    }

    public void setSeasons(int seasons) {
        this.seasons = seasons;
    }

    // private CreatorNode creator;
    //private CollectorNode collector;
    private MigratorNode collector;
    List<List<BreederNode>> breeders = new LinkedList<List<BreederNode>>();
    List<MigratorNode> migrators = new LinkedList<MigratorNode>();
    private boolean generated = false;

    public void generateGraph() {
        if (generated) {
            return;
        }
        // collector = new CollectorNode(this.islands, this.epochs);
        collector = new MigratorNode(epochs, migrationWidth);
        collector.setExecutable(this.migratorExec);

        for (int i = 1; i < epochs; i++) {
            MigratorNode mig = new MigratorNode(i, migrationWidth);
            mig.setExecutable(this.migratorExec);
            migrators.add(mig);
        }
        for (int i = 1; i <= epochs; i++) {
            List<BreederNode> list = new LinkedList<BreederNode>();
            breeders.add(list);
            for (int j = 1; j <= islands; j++) {
                BreederNode bread = new BreederNode(j, i, seasons, islands, migrationWidth);
                bread.setExecutable(this.breederExec);
                list.add(bread);
            }
        }
        if (epochs == 1) {
            for (int j = 1; j <= islands; j++) {
                BreederNode br = breeders.get(0).get(j - 1);
                br.addOutputNodes(collector);
                collector.addInputNode(br);
            }
        } else {
            int epoch = 0;
            for (List<BreederNode> breederEpoch : breeders) {
                int island = 0;
                MigratorNode previous = epoch > 0 ? migrators.get(epoch - 1) : null;
                MigratorNode next = epoch < epochs - 1 ? migrators.get(epoch) : null;
                for (BreederNode br : breederEpoch) {
                    if (next != null) {
                        next.addInputNode(br);
                        br.addOutputNodes(next);
                    }
                    if (previous != null) {
                        previous.addOutputNodes(br);
                        br.addInputNode(previous);
                    }
                    island++;
                }
                epoch++;
            }
            for (BreederNode br : breeders.get(breeders.size() - 1)) {
                collector.addInputNode(br);
                br.addOutputNodes(collector);
            }
        }
    }

    @Override
    public String toString() {

        if (!generated) {
            generateGraph();
            generated = true;
        }

        StringBuilder buffer = new StringBuilder();
        buffer.append("[\n");
        buffer.append("Type = " + type + ";\n");
        buffer.append("VirtualOrganisation = " + getVO() + ";\n");
        buffer.append("InputSandbox = " + getInputSandbox() + ";\n");
        buffer.append("Nodes = [\n");
        // buffer.append(creator);
        for (List<BreederNode> breeder1 : breeders) {
            for (BreederNode breeder : breeder1) {
                buffer.append(breeder);
            }
        }
        for (MigratorNode migrator : migrators) {
            buffer.append(migrator);
        }
        buffer.append(collector);
        buffer.append("];\n");

        buffer.append("Dependencies = {\n");

        for (List<BreederNode> brs : breeders) {
            for (BreederNode br : brs) {
                for (Node input : br.inputNodes) {
                    buffer.append("{" + input.getName() + ", " + br.getName() + "},\n");
                }
                for (Node output : br.outputNodes) {
                    buffer.append("{" + br.getName() + ", " + output.getName() + "},\n");
                }
            }
        }
        buffer.delete(buffer.length() - 2, buffer.length() - 1);
        buffer.append("};\n");

        String req = getRequirements();
        if (req != null) {
            buffer.append("Requirements = " + req + ";\n");
        }
        buffer.append("Rank = " + getRank() + ";\n");
        buffer.append("DefaultNodeRetryCount = " + getDefaultNodeRetryCount() + ";\n");
        buffer.append("DefaultNodeShallowRetryCount = " + getDefaultNodeShallowRetryCount() + ";\n");

        buffer.append("]");
        return buffer.toString();

    }
}
